#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "iot_watchdog.h"
#include "../inc/iot_gpio_ex.h"
#include "../inc/sle_serve_all.h"
#include "pinctrl.h"

/**
 * @brief Global variables for storing pulse count, water volume, and amount.
 *
 * These global variables are used to keep track of the number of pulses detected,
 * the total volume of water measured, the volume in liters, and the associated monetary amount.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */

// Global variable to store the number of pulses detected
float Puls = 0.0f;

// Global variable to store the total volume of water measured (in a suitable unit)
float Water = 0.0f;

// Global variable to store the volume of water in liters
float Water_L = 0.0f;

// Global variable to store the monetary amount associated with the water usage
float Amount = 0.0f;

/**
 * @brief Initializes and resets the slide mechanism.
 *
 * This function sets up the GPIO pin connected to the slide mechanism:
 * 1. Initializes the GPIO pin.
 * 2. Sets the alternate function for the GPIO pin.
 * 3. Configures the GPIO pin as an output direction.
 * 4. Sets the GPIO pin to pull-up mode.
 *
 * Note: The original code set the GPIO direction to input, which is corrected here to output.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void Slide_Reset(void)
{
    // Initialize the GPIO pin connected to the slide mechanism
    IoTGpioInit(IOT_GPIO_IDX_slide);

    // Set the alternate function for the GPIO pin (if necessary)
    IoSetFunc(IOT_GPIO_IDX_slide, IOT_IO_FUNC_GPIO_9_GPIO);

    // Configure the GPIO pin as an output direction
    IoTGpioSetDir(IOT_GPIO_IDX_slide, IOT_GPIO_DIR_OUT);

    // Optionally, if you need to control the initial state of the pin:
    // Set the GPIO pin to a high level (assuming active low for reset)
    IoTGpioSetOutputVal(IOT_GPIO_IDX_slide, IOT_GPIO_VALUE1);

    // If pull-up is required for some reason (e.g., for debouncing or other purposes),
    // set the GPIO pin to pull-up mode.
    // However, for driving external hardware directly, this might not be necessary.
    IoSetPull(IOT_GPIO_IDX_slide, IOT_IO_PULL_UP);
}

#include <stdio.h>

/**
 * @brief Calculates the amount based on water usage and Zhengzhou's water price.
 *
 * This function calculates the monetary amount for a given volume of water (in liters)
 * using the water price of 9.65 yuan per cubic meter in Zhengzhou.
 *
 * @param water_l Volume of water in liters.
 * @return The calculated monetary amount.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
float calculate_amount(float water_l)
{
    return (water_l / 1000) * 9.65; // 1 cubic meter equals 1000 liters
}

/**
 * @brief Retrieves and processes water flow data.
 *
 * This function performs the following tasks:
 * 1. Kicks the watchdog timer to prevent system lockup due to program anomalies.
 * 2. Calculates the instantaneous water volume based on the pulse count.
 * 3. Updates the `waterline` string with the current water volume.
 * 4. Calculates the cumulative water volume in cubic meters and the associated monetary amount.
 * 5. Updates the `amountline` string with the current monetary amount.
 *
 * @param waterline Pointer to a character array where the formatted water volume will be stored.
 * @param amountline Pointer to a character array where the formatted monetary amount will be stored.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void yfs201c_get_data(char *waterline, char *amountline)
{
    IoTWatchDogKick(); // Kick the watchdog timer to prevent system hang

    // Calculate instantaneous water volume (liters)
    Water = Puls / 300.00;
    update_waterline(waterline, Water); // Update the waterline string with the current water volume

    // Calculate cumulative water volume (cubic meters) and amount
    Water_L = Water / 1000;
    Amount = calculate_amount(Water_L);
    update_amountline(amountline, Amount); // Update the amountline string with the current monetary amount
}

#include <stdio.h>

/**
 * @brief Updates the water volume display string.
 *
 * This function formats the given water volume into a string and stores it in the provided buffer.
 * It uses `sprintf` to format the water volume with two decimal places followed by "L".
 * If the formatting fails, it prints an error message.
 *
 * @param buffer Pointer to the character array where the formatted water volume will be stored.
 * @param water The water volume in liters to be formatted.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void update_waterline(char *buffer, float water)
{
    // Format the water volume as a string with two decimal places followed by "L"
    int ret = sprintf(buffer, ": %.2fL", water);
    
    // Check if the formatting was successful
    if (ret < 0)
    {
        osal_printk("Waterline update failed\r\n");
    }
    else
    {
        // Optionally, you can add additional checks or actions here if needed
        // For example, ensure the buffer is null-terminated
        buffer[ret] = '\0';
    }
}

#include <stdio.h>

/**
 * @brief Updates the monetary amount display string.
 *
 * This function formats the given monetary amount into a string and stores it in the provided buffer.
 * It uses `snprintf` to format the amount with two decimal places followed by "￥".
 * If the formatting fails or the buffer is too small, it prints an error message.
 *
 * @param buffer Pointer to the character array where the formatted amount will be stored.
 * @param bufferSize Size of the buffer to prevent overflow.
 * @param amount The monetary amount to be formatted.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void update_amountline(char *buffer, size_t bufferSize, float amount)
{
    // Ensure the buffer has enough space for the formatted string plus null terminator
    if (bufferSize <= 0) {
        osal_printk("Invalid buffer size\r\n");
        return;
    }

    // Use snprintf to prevent buffer overflow
    int ret = snprintf(buffer, bufferSize, ": %.2f￥", amount);

    // Check if the formatting was successful and did not truncate
    if (ret < 0 || (size_t)ret >= bufferSize)
    {
        osal_printk("Amountline update failed or truncated\r\n");
    }
}

/**
 * @brief GPIO callback function for pulse counting.
 *
 * This function is called whenever a GPIO interrupt occurs, indicating a new pulse.
 * It increments the global pulse count and prints the current pulse count to the console.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void gpio_callback_func(void)
{
    // Increment the pulse count
    Puls += 1;

    // Print the current pulse count to the console
    osal_printk("Puls = %f\r\n", Puls);

    // Optionally, you can add more processing here if needed
}

/**
 * @brief Task to monitor water flow and calculate the amount.
 *
 * This task registers an interrupt service routine (ISR) for pulse counting,
 * monitors water flow, calculates the amount based on water usage,
 * updates the display strings for water volume and monetary amount,
 * and prints these values to the console periodically.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void SlideTask(void)
{
    static char waterline[32] = {0};
    static char amountline[32] = {0};

    // Register the GPIO ISR function for edge-triggered interrupts on the slide pin
    uapi_gpio_register_isr_func(IOT_GPIO_IDX_slide, IOT_INT_TYPE_EDGE, gpio_callback_func);

    // Optionally, if you need more specific configuration:
    //IoTGpioRegisterIsrFunc(IOT_GPIO_IDX_slide,                 // GPIO-9 引脚
    //                       IOT_INT_TYPE_EDGE,                  // 边沿触发
    //                       IOT_GPIO_EDGE_FALL_LEVEL_LOW,       // 下降沿触发
    //                       gpio_callback_func,                 // 中断处理函数
    //                       NULL);                              // 中断处理函数的参数

    while (1)
    {
        IoTWatchDogKick(); // Kick the watchdog timer to prevent system hang

        // Retrieve and process water flow data
        yfs201c_get_data(waterline, amountline);

        // Update the amount display string
        update_amountline(amountline, sizeof(amountline), Amount);

        // Output water volume and amount
        osal_printk("Water = %s\r\n", waterline);
        osal_printk("Amount = %s\r\n", amountline);

        // Delay for 1 second before the next monitoring cycle
        osDelay(1000); // 1000 milliseconds = 1 second
    }
}